#include "network_frame.h"

#define MAX_BUF_SIZE 1024

#define container_of(ptr, type, member) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})

#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

#define list_next_entry(pos, member) \
	list_entry((pos)->member.next, typeof(*(pos)), member)

#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     &pos->member != (head); 	\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != NULL; pos = pos->next)


#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; pos != (head); \
		pos = n, n = pos->next)

#define list_for_each_entry_safe(pos, n, head, member)			\
	for (pos = list_first_entry(head, typeof(*pos), member),	\
		n = list_next_entry(pos, member);			\
	     &pos->member != (head); 					\
	     pos = n, n = list_next_entry(n, member))

static inline void INIT_LIST_HEAD(struct list_head *head)
{
    head->next = head;
    head->prev = head;
}

static inline void __list_add(struct list_head *node,
                              struct list_head *prev,
                              struct list_head *next)
{
    next->prev = node;
    node->next = next;
    node->prev = prev;
    prev->next = node;
}

static inline void list_add(struct list_head *node, struct list_head *head)
{
    __list_add(node, head, head->next);
}

static inline void list_add_tail(struct list_head *node, struct list_head *head)
{
    __list_add(node, head->prev, head);
}

static inline void __list_del(struct list_head* prev, struct list_head* next)
{
    next->prev = prev;
    prev->next = next;
}

static inline void list_del(struct list_head *entry)
{
    __list_del(entry->prev, entry->next);
    entry->next = NULL;
    entry->prev = NULL;
}

static int tcp_connect(struct conn_t* c, struct sockaddr_in* addr)
{
    int ret;

    do {
        ret = ::connect(c->fd, (struct sockaddr*)addr, sizeof(struct sockaddr_in));
    } while (ret < 0 && errno == EINTR);

    return 0;
}

static ssize_t tcp_recv_msg(struct conn_t* c, void *buf, size_t len)
{
    int nbytes;

    do {
        nbytes = ::recv(c->fd, buf, len, 0);
    } while ((nbytes == -1) && (errno == EINTR));    

    return nbytes;
}

static ssize_t tcp_send_msg(struct conn_t* c, const void *buf, size_t len)
{
    int nbytes;

    do {
        nbytes = ::send(c->fd, buf, len, 0);
    } while ((nbytes == -1) && (errno == EINTR));  

    return nbytes;  
}

ssize_t tcp_send_msg_to(struct conn_t* c, const void *buf, size_t len, 
                        const struct sockaddr *dest_addr __attribute__((unused)), 
                        socklen_t addrlen __attribute__((unused)))
{
    return tcp_send_msg(c, buf, len);
}

static ssize_t udp_recv_msg(struct conn_t* c, void *buf, size_t len)
{
    int nbytes;
    socklen_t socklen = sizeof(struct sockaddr_in);

    do {
        nbytes = ::recvfrom(c->fd, buf, len, 0, (struct sockaddr*)&c->addr, &socklen);
    } while ((nbytes == -1) && (errno == EINTR));    

    return nbytes;
}

ssize_t udp_send_msg_to(struct conn_t* c, const void *buf, size_t len, const struct sockaddr *dest_addr, socklen_t addrlen)
{
    int nbytes;

    do {
        nbytes = ::sendto(c->fd, 
                          buf, 
                          len, 
                          0, 
                          dest_addr, 
                          addrlen);

    } while ((nbytes == -1) && (errno == EINTR));  

    return nbytes;     
}

static ssize_t udp_send_msg(struct conn_t* c, const void *buf, size_t len)
{
    return udp_send_msg_to(c, buf, len, (struct sockaddr*)&c->addr, sizeof(struct sockaddr_in));
}

static struct sock_io_ops tcp_sock_ops = {
                tcp_connect,
                tcp_recv_msg,
                tcp_send_msg,
                tcp_send_msg_to
};

static struct sock_io_ops udp_sock_ops = {
                NULL,
                udp_recv_msg,
                udp_send_msg,
                udp_send_msg_to
};

static void add_fd(struct conn_context* ctxt, int fd)
{
    FD_SET(fd, &ctxt->sop.rset);
    ctxt->sop.nr_fd ++;

    if (fd > ctxt->sop.maxfd)
        ctxt->sop.maxfd = fd;
}

static void del_fd(struct conn_context* ctxt, int fd)
{
    FD_CLR(fd, &ctxt->sop.rset);
    ctxt->sop.nr_fd --;
}

int conn_context_init(struct conn_context* ctxt)
{
    ctxt->srv.listen_fd = -1;
    ctxt->srv.cb = NULL;
    ctxt->srv.sock_ops = NULL;

    ctxt->sop.maxfd = -1;
    ctxt->sop.nr_fd = 0;
    FD_ZERO(&ctxt->sop.rset);
    INIT_LIST_HEAD(&ctxt->sop.list);
    ctxt->sop.nr_conn = 0;
    
    return 0;
}

int conn_context_destory(struct conn_context* ctxt)
{
    if (ctxt->srv.listen_fd != -1)
        server_shutdown(ctxt);

    struct conn_t* pos;
    list_for_each_entry(pos, &ctxt->sop.list, list) {
        ::close(pos->fd);
        free(pos);
    }
    
    return 0;    
}

int tcp_server_init(struct conn_context* ctxt, struct sockaddr_in* addr, struct conn_cb_t* cb)
{
    int ret;

    if (ctxt->srv.listen_fd != -1)
        return -1;

    ctxt->srv.listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    add_fd(ctxt, ctxt->srv.listen_fd);
    ctxt->srv.cb = cb;
    ctxt->srv.sock_ops = &tcp_sock_ops;

    struct linger lingerbuf;  
    lingerbuf.l_onoff = 0; 
    lingerbuf.l_linger = 0; 

    ret = ::setsockopt(ctxt->srv.listen_fd, SOL_SOCKET, SO_LINGER, (char*)&lingerbuf, sizeof(lingerbuf));
    if (ret == -1) {
        printf("setsockopt() for SO_LINGER failed on fd=%d with error[%d]='%s'",
                ctxt->srv.listen_fd,
                errno,
                strerror(errno));

        return -1;
    }

    int reuseval = 1; 
    ret = ::setsockopt(ctxt->srv.listen_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseval, sizeof(reuseval));
    if (ret == -1) {
        printf("setsockopt() for SO_REUSEADDR failed on fd=%d with error[%d]='%s'",
                ctxt->srv.listen_fd,
                errno,
                strerror(errno));

        return -1;
    }

    ret = ::bind(ctxt->srv.listen_fd, (struct sockaddr*)addr, sizeof(struct sockaddr_in));
    if (ret == -1) {
        printf("bind() failed on fd=%d with error[%d]='%s'",
                ctxt->srv.listen_fd,
                errno,
                strerror(errno));

        return -1;
    }

    int oflags;
    if ((oflags = fcntl(ctxt->srv.listen_fd, F_GETFL, 0)) == -1) {
        printf("fcntl F_GETFL: errno=[%d:%s]\n", errno, strerror(errno));
        return -1;
    }

    if (fcntl(ctxt->srv.listen_fd, F_SETFL, oflags|O_NONBLOCK) == -1) {
        printf("fcntl F_SETFL: errno=[%d:%s]\n", errno, strerror(errno));
        return -1;
    }
    
    ret = ::listen(ctxt->srv.listen_fd, 5);
    if (ret == -1) {
        printf("listen() failed on fd=%d with error[%d]='%s'",
                ctxt->srv.listen_fd,
                errno,
                strerror(errno));

        return -1;
    }    

    return 0;
}

int server_shutdown(struct conn_context* ctxt)
{
    ::close(ctxt->srv.listen_fd);
    ctxt->srv.listen_fd = -1;
    ctxt->srv.cb = NULL;

    return 0;
}

static struct conn_t* alloc_and_enq_conn(struct conn_context* ctxt, int fd, struct conn_cb_t* cb, struct sock_io_ops* ops)
{
    struct conn_t* c = (struct conn_t*)malloc(sizeof(struct conn_t));
    
    c->fd = fd;
    c->cb = cb;
    c->ops = ops;
    add_fd(ctxt, c->fd);
    list_add(&c->list, &ctxt->sop.list);
    ctxt->sop.nr_conn ++;

    return c;    
}

struct conn_t* make_tcpconn_to(struct conn_context* ctxt, struct sockaddr_in* addr, struct conn_cb_t* cb)
{
    int fd;
    struct conn_t* c = NULL; 

    if ((fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
        return NULL;
    }

    int oflags;
    if ((oflags = fcntl(fd, F_GETFL, 0)) == -1) {
        printf("fcntl F_GETFL: errno=[%d:%s]\n", errno, strerror(errno));
        return NULL;
    }

    if (fcntl(fd, F_SETFL, oflags|O_NONBLOCK) == -1) {
        printf("fcntl F_SETFL: errno=[%d:%s]\n", errno, strerror(errno));
        return NULL;
    }

    c = alloc_and_enq_conn(ctxt, fd, cb, &tcp_sock_ops);

    c->ctxt = ctxt;
    c->ops->connect(c, addr);

    return c;
}

struct conn_t* make_udpconn_to(struct conn_context* ctxt, struct sockaddr_in* addr, struct conn_cb_t* cb)
{
    int fd;
    struct conn_t* c = NULL; 

    if ((fd = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
        return NULL;
    }

    c = alloc_and_enq_conn(ctxt, fd, cb, &udp_sock_ops);
    c->ctxt = ctxt;
    memcpy((void*)&c->addr, (void*)addr, sizeof(struct sockaddr_in));

    return c;
    
}

int bind_local_address(struct conn_t* c, struct sockaddr_in* addr)
{
    int ret;

    ret = ::bind(c->fd, (struct sockaddr*)addr, sizeof(struct sockaddr_in));
    if (ret == -1) {
        printf("bind() failed on fd=%d with error[%d]='%s'\n",
                c->fd,
                errno,
                strerror(errno));

        return -1;
    }

    return 0; 
}

int release_connection(struct conn_t* c)
{
    del_fd(c->ctxt, c->fd);
    list_del(&c->list);
    c->ctxt->sop.nr_conn --;

    close(c->fd);

    free(c);

    return 0;
}

int send_msg(struct conn_t* c,  const char* buf, ssize_t len)
{
    return c->ops->send(c, buf, len);
}

int send_msg_to(struct conn_t* c, const char* buf, ssize_t len, const struct sockaddr *dest_addr, socklen_t addrlen)
{
     return c->ops->sendto(c, buf, len, dest_addr, addrlen);
}

int recv_msg(struct conn_t* c, char* buf, ssize_t len)
{
    return c->ops->recvive(c, buf, len);
}

int main_loop(struct conn_context* ctxt)
{
    fd_set rfds;
    ssize_t nbytes;
    int ret;
    int fd;  
    struct conn_t* pos;
    struct conn_t* next;
    char buf[MAX_BUF_SIZE];

    struct timeval tv;


    for (;;) {
        FD_ZERO(&rfds);
        rfds = ctxt->sop.rset;
        tv.tv_usec = 0;
        tv.tv_sec = 2;
        do {
            ret = select(ctxt->sop.maxfd + 1, &rfds, NULL, NULL, &tv);
        } while (ret < 0 && errno == EINTR);

        if (ret < 0) {
            return -1;
        }

        if (ctxt->srv.listen_fd != -1 && 
            FD_ISSET(ctxt->srv.listen_fd, &rfds)) {
            fd = accept(ctxt->srv.listen_fd, NULL, NULL);
            // make new connection
            (void)alloc_and_enq_conn(ctxt, fd, ctxt->srv.cb, ctxt->srv.sock_ops);
        }

        list_for_each_entry_safe(pos, next, &ctxt->sop.list, list) {
            if (FD_ISSET(pos->fd, &rfds)) {
                if ((nbytes = recv_msg(pos, buf, MAX_BUF_SIZE)) > 0) {
                    if (pos->cb->func != NULL)
                        pos->cb->func(pos, buf, nbytes);
                } else if (nbytes == 0){
                    if (pos->cb->on_close != NULL)
                        pos->cb->on_close(pos);
                } else {
                    if (pos->cb->on_error != NULL)
                         pos->cb->on_error(pos);
                }
            }
        }
    }

    return 0;    
}

